//
//  DPNotificationObserver.m
//  UITestingKit
//
//  Created by Ofri Wolfus on 15/03/07.
//  Copyright 2007 Ofri Wolfus. All rights reserved.
//

#import "DPNotificationObserver.h"
#import <UITestingKit/DPRunLoopSource.h>


@interface DPNotificationObserver : DPRunLoopSource
- (void)processNotification:(NSNotification *)notif;
@end

@implementation DPNotificationObserver

- (void)invalidate {
	// Stop the nested loop
	CFRunLoopStop(CFRunLoopGetCurrent());
	// Unregister with the current runloop
	[super invalidate];
}

// Invoked when the requested notification is received.
- (void)processNotification:(NSNotification *)notif {
	[self invalidate];
}

// The notification hasn't been received yet but our timeout
// hour has arrived.
- (void)observerTimedOut:(NSTimer *)timer {
	[self invalidate];
}

@end

@implementation NSNotificationCenter (DPNotificationObserver)

- (void)waitForNotification:(NSString *)name
					 object:(id)object
{
	[self waitForNotification:name
					   object:object
					  timeout:0.0];
}

- (BOOL)waitForNotification:(NSString *)name
					 object:(id)object
					timeout:(NSTimeInterval)sec
{
	// Create a new observer
	DPNotificationObserver *obs = [[DPNotificationObserver alloc] init];
	NSTimer *timer = nil;
	BOOL result = YES;
	
	// Register it with our notification center
	[self addObserver:obs
			 selector:@selector(processNotification:)
				 name:name
			   object:object];
	
	// Set up a timeout timer if needed
	if (sec > 0.0)
		timer = [[NSTimer scheduledTimerWithTimeInterval:sec
												  target:obs
												selector:@selector(observerTimedOut:)
												userInfo:nil
												 repeats:NO] retain];


	
	// Add the observer to the current runloop so it stays
	// active even if there are no observers.
	// Although we register a runloop source, we never signal it
	// so the runloop can sit idle.
	[[NSRunLoop currentRunLoop] addSource:obs forMode:NSDefaultRunLoopMode];
	// Run the runloop indefinitely. This nests a loop in the default mode
	// which will be stopped when the notification is received.
	CFRunLoopRun();
	
	// Clean up
	if (timer) {
		// The timer is valid only if we hadn't timed out
		result = [timer isValid];
		[timer invalidate]; [timer release];
	}
	[self removeObserver:obs];
	[obs release];
	
	return result;
}

@end
